package com.zyt.wiki.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zyt.wiki.resp.UserLoginResp;
import com.zyt.wiki.util.LoginUserContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;
/*
 拦截器：Spring框架特有的，常用于登录校验，权限校验，请求日志打印
【方法执行顺序】当进入拦截器链中的某个拦截器，并执行preHandle方法后
    1.当preHandle方法返回false时，从当前拦截器往回执行所有拦截器的afterCompletion方法，再退出拦截器链。也就是说，请求不继续往下传了，直接沿着来的链往回跑。
    2.当preHandle方法返回true时，执行下一个拦截器,直到所有拦截器执行完。再运行被拦截的Controller。然后进入拦截器链，运行所有拦截器的postHandle方法,完后从最后一个拦截器往回执行所有拦截器的afterCompletion方法.
*/
/**
 * 【登录校验】 针对登录后交易，校验登录凭证，若通过则同时将User对象放入ThreadLocal中
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

    public static final Logger LOG = LoggerFactory.getLogger(LoginInterceptor.class) ;

    @Resource
    private RedisTemplate redisTemplate ;

    /** 预处理 */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        LOG.info("------------- LoginInterceptor 开始 -------------");
        long startTime = System.currentTimeMillis() ;
        request.setAttribute("requestStartTime", startTime);

        // OPTIONS请求不做校验（前后端分离的架构, 前端会发一个OPTIONS请求先做预检, 对预检请求不做校验）
        if(request.getMethod().toUpperCase().equals("OPTIONS")){
            return true ; // 返回true，请求会继续往后；返回false，则请求中断
        }

        String path = request.getRequestURL().toString() ;
        LOG.info("登录权限拦截，path: {}", path);

        // 获取header的token参数
        String token = request.getHeader("token");
        LOG.info("登录校验开始，token: {}", token);
        if(token == null || token.isEmpty()){
            LOG.info("上送token为空，请求被拦截");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false ;
        }
        Object obj = this.redisTemplate.opsForValue().get(token);
        if(obj == null){
            LOG.info("上送token无效，请求被拦截");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false ;
        }else {
            // 登录账号互踢 begin ---
            UserLoginResp userLoginResp = JSONObject.parseObject((String) obj, UserLoginResp.class);
            String loginName = userLoginResp.getLoginName();
            String userLoginToken = (String) this.redisTemplate.opsForValue().get(loginName);
            if(!token.equals(userLoginToken)){
                LOG.info("上送token不是最新的，请求被拦截");
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                return false ;
            }
            // 登录账号互踢 end ----
            LOG.info("已登录：{}", obj) ;
            // 刷新登录凭证的有效时间
            this.redisTemplate.expire(token, 60 * 15, TimeUnit.SECONDS) ;
            // 將Redis中的用户信息转回对象，并放入ThreadLocal中。 供后面的ActionInterceptor拦截器判断当前用户是谁。
            LoginUserContext.setUser(JSON.parseObject((String)obj, UserLoginResp.class));
            return true;
        }
    }

    /** 返回处理 */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        long startTime = (long) request.getAttribute("requestStartTime");
        LOG.info("------------- LoginInterceptor 结束 耗时: {} ms -------------", System.currentTimeMillis() - startTime);
    }

    /** 后处理 */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//        LOG.info("LoginInterceptor 结束");
    }
}
